/*
 * DistanceFunction.h
 *
 *  Created on: Jan 15, 2011
 *      Author: LyonsDesktop
 */

#ifndef DISTANCEFUNCTION_H_
#define DISTANCEFUNCTION_H_

#include "../../Datasets/Dataset.h"
#include <vector>
#include <cmath>
#include <cstdlib>

using namespace CLEVER::Datasets;

namespace CLEVER {
namespace RegionDiscovery {
namespace DistanceFunctions {
//! Abstract base class to computes distances.
template<typename T>
class DistanceFunction
{
public:
	//! Compute the distances between two examples
	/*!
	   \param d1 Dataset containing the first example
	   \param i1 Index of the first example in the dataset
	   \param d2 Dataset containing the second example
	   \param i2 Index of the second example in the dataset
	 */
	inline virtual double Distance(Dataset<T> * d1, int i1, Dataset<T> * d2, int i2) = 0;

	//! Compute the distances between an example and two other examples using SSE
	/*!
	   \param d1 Dataset containing the first example
	   \param i1 Index of the first example in the first dataset
	   \param i1b Index of the second example in the first dataset
	   \param d2 Dataset containing the third example
	   \param i2 Index of the third example in the dataset
	   \param di12 The first distance
	   \param di13 The second distance
	   \param Relative Return relative distances
	 */
	inline virtual void DistanceSSE(NumericDataset * d1, int i1, int i1b, NumericDataset * d2, int i2, double & di12, double & di13, bool Relative = false)
	{ di12 = di13 = -1; }
	inline virtual void DistanceSSE(NumericDataset * d1, int i1, int i1b, DoubleArrayDataset * d2, int i2, double & di12, double & di13, bool Relative = false)
	{ di12 = di13 = -1; }
};

class MinkowskiDistanceFunction : public DistanceFunction<double>
{
public:
	double exp, invexp;

	MinkowskiDistanceFunction(double exp)
		: exp(exp), invexp(1.0 / exp)
	{
	}


	//! See: DistanceFunction::Distance
	virtual double Distance(NumericDataset * d1, int i1, NumericDataset * d2, int i2);

	//! See: DistanceFunction::DistanceSSE
	virtual void DistanceSSE(NumericDataset * d1, int i1, int i1b, NumericDataset * d2, int i2, double & di12, double & di13, bool Relative);

	virtual void DistanceSSE(NumericDataset * d1, int i1, int i1b, DoubleArrayDataset * d2, int i2, double & di12, double & di13, bool Relative);

};

class ZeroOneNormalizedDistanceFunction : public MinkowskiDistanceFunction
{
public:
	NumericDataset * _normalized_dataset;

	ZeroOneNormalizedDistanceFunction(double rExponent, NumericDataset * _normalized_dataset)
		: _normalized_dataset(_normalized_dataset), MinkowskiDistanceFunction(rExponent)
	{

	}


	//! See: DistanceFunction::Distance
	__inline__ virtual double Distance(NumericDataset * d1, int i1, NumericDataset * d2, int i2)
	{
		double udist = 0;

		for (int i = 0; i < d1->GetNumAttributes(); i++) {
			//double x = _normalized_dataset->GetValue(i1, i);
			//double y = (d2->GetValue(i2, i) - d1->GetAttribute(i).dmin)
			//          /
			//         (d1->GetAttribute(i).dmax - d1->GetAttribute(i).dmin);

			double tdiff = 1;            //abs(x - y);
			udist += pow(tdiff, exp);
		}
		udist = pow(udist, invexp);
		return udist;
	}


	__inline__ virtual double Distance(NumericDataset * d1, int i1, DoubleArrayDataset * d2, int i2)
	{
		double udist = 0;

		for (int i = 0; i < d1->GetNumAttributes(); i++) {
			//double x = _normalized_dataset->GetValue(i1, i);
			//double y = (d2->GetValue(i2, i) - d1->GetAttribute(i).dmin)
			//           /
			//           (d1->GetAttribute(i).dmax - d1->GetAttribute(i).dmin);

			double tdiff = 1;            //abs(x - y);
			udist += pow(tdiff, exp);
		}
		udist = pow(udist, invexp);
		return udist;
	}


};

class Z_NormalizedDistanceFunction : public MinkowskiDistanceFunction
{
public:
	std::vector<double> std_dev;

	Z_NormalizedDistanceFunction(double rExponent, std::vector<double> & std_dev)
		: std_dev(std_dev), MinkowskiDistanceFunction(rExponent)
	{

	}


	//! See: DistanceFunction::Distance
	__inline__ virtual double Distance(NumericDataset * d1, int i1, NumericDataset * d2, int i2)
	{
		double udist = 0;

		for (int i = 0; i < d1->GetNumAttributes(); i++) {
			double x = d1->GetValue(i1, i);
			double y = d2->GetValue(i2, i);
			double tdiff = abs(x - y) / std_dev[i];
			udist += pow(tdiff, exp);
		}
		udist = pow(udist, invexp);
		return udist;
	}


	__inline__ virtual double Distance(NumericDataset * d1, int i1, DoubleArrayDataset * d2, int i2)
	{
		double udist = 0;

		for (int i = 0; i < d1->GetNumAttributes(); i++) {
			double x = d1->GetValue(i1, i);
			double y = d2->GetValue(i2, i);
			double tdiff = abs(x - y) / std_dev[i];
			udist += pow(tdiff, exp);
		}
		udist = pow(udist, invexp);
		return udist;
	}


};
}
}
}

#endif /* DISTANCEFUNCTION_H_ */
